package GMDPRE

import (
	"fmt"
	"time"
)

var testJac2GMont = JacPoint{
	BN{0xe63c48d6, 0x2eda8381, 0xd5b614a6, 0x3243d78, 0x26014409, 0x7aef3e86, 0x76a3b1f7, 0x398874c4},
	BN{0xc1a73e40, 0x28385681, 0xf6cf4dc1, 0x29f04a88, 0x64921d48, 0x5a86a7ae, 0xdaf4fd47, 0x1fbbdfdd},
	BN{0x785a1bba, 0x826a9cb2, 0x1a652bf5, 0x83ebcaf1, 0xdc5491f1, 0x1a99f60c, 0x3ae6b7b, 0xc79acba9}}

var testJac3GMont = JacPoint{
	BN{0x53498d0e, 0x5294c0f, 0xc7c50abc, 0x3c51eb40, 0xbdf037e4, 0x5dd76fbc, 0x77c8c028, 0x3015ad20},
	BN{0x7bf1733c, 0xd40bb89, 0xc6242c3c, 0x9efa98a8, 0x7ce702b4, 0x86371f71, 0xde1a1105, 0x167df558},
	BN{0x2c29b331, 0x51711cc3, 0x7eef2b43, 0x5832932b, 0x90368de2, 0x2c20fad, 0xf942174e, 0xe3d2125a}}

var testPriKeyBytesStd = []uint8{
	0x12, 0x8B, 0x2F, 0xA8, 0xBD, 0x43, 0x3C, 0x6C, 0x06, 0x8C, 0x8D, 0x80, 0x3D, 0xFF, 0x79, 0x79,
	0x2A, 0x51, 0x9A, 0x55, 0x17, 0x1B, 0x1B, 0x65, 0x0C, 0x23, 0x66, 0x1D, 0x15, 0x89, 0x72, 0x63}
var testPubKeyBytesStd = []uint8{
	0xD5, 0x54, 0x8C, 0x78, 0x25, 0xCB, 0xB5, 0x61, 0x50, 0xA3, 0x50, 0x6C, 0xD5, 0x74, 0x64, 0xAF,
	0x8A, 0x1A, 0xE0, 0x51, 0x9D, 0xFA, 0xF3, 0xC5, 0x82, 0x21, 0xDC, 0x81, 0x0C, 0xAF, 0x28, 0xDD,
	0x92, 0x10, 0x73, 0x76, 0x8F, 0xE3, 0xD5, 0x9C, 0xE5, 0x4E, 0x79, 0xA4, 0x94, 0x45, 0xCF, 0x73,
	0xFE, 0xD2, 0x30, 0x86, 0x53, 0x70, 0x27, 0x26, 0x4D, 0x16, 0x89, 0x46, 0xD4, 0x79, 0x53, 0x3E}
var testZa = []uint8{
	0xE6, 0xE8, 0x31, 0xE4, 0x6D, 0x33, 0x83, 0x22, 0xF4, 0x31, 0xED, 0x5A, 0xC3, 0x36, 0x44, 0x83,
	0xE9, 0x37, 0x2D, 0x4B, 0x77, 0x95, 0xEF, 0x54, 0x5D, 0x68, 0xE9, 0x1C, 0x58, 0x3A, 0x66, 0x93}
var testMsg = []uint8{
	0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74}
var testHashStd = []uint8{
	0x10, 0xD5, 0x1C, 0xB9, 0x0C, 0x0C, 0x05, 0x22, 0xE9, 0x48, 0x75, 0xA2, 0xBE, 0xA7, 0xAB, 0x72,
	0x29, 0x9E, 0xBE, 0x71, 0x92, 0xE6, 0x4E, 0xFE, 0x05, 0x73, 0xB1, 0xC7, 0x71, 0x10, 0xE5, 0xC9}
var testRnd = []uint8{
	0x34, 0x91, 0x4C, 0x20, 0x25, 0x1A, 0x59, 0xA2, 0xC3, 0x11, 0x10, 0x29, 0x44, 0xC6, 0x00, 0x43,
	0x0A, 0x02, 0x28, 0x5A, 0x04, 0x33, 0x14, 0x42, 0x28, 0x14, 0x2A, 0x18, 0x48, 0x00, 0x4C, 0x14}
var testSignStd = []uint8{
	0x91, 0x9D, 0x1E, 0xE4, 0x0D, 0x1C, 0x49, 0xC4, 0x22, 0x6B, 0x86, 0x6F, 0xFA, 0x26, 0x1D, 0xA0,
	0x6D, 0xEA, 0x27, 0x4A, 0x44, 0xF4, 0xE3, 0x88, 0x8A, 0x8B, 0x97, 0x84, 0xFC, 0xCA, 0x0A, 0xFA,
	0xAB, 0x01, 0xA6, 0x58, 0x60, 0xD8, 0xD9, 0xAB, 0xFF, 0x23, 0xCE, 0xD9, 0x52, 0xC1, 0x9A, 0xCB,
	0x94, 0x04, 0x85, 0x63, 0x22, 0x4A, 0x2D, 0x3C, 0xF8, 0x85, 0x4A, 0x35, 0x69, 0xF6, 0xD2, 0x5B}

func testEcdlpDoublePoint() {
	var res = JacPoint{BN{1}, BN{1}, BN{0}}

	DoubleJacP2JacP(&res, &Sm2JacG)

	var a = BnEqual(&(testJac2GMont.x), &(res.x), Sm2CurveDigit)
	var b = BnEqual(&(testJac2GMont.y), &(res.y), Sm2CurveDigit)
	var c = BnEqual(&(testJac2GMont.z), &(res.z), Sm2CurveDigit)

	fmt.Printf("Test DoubleJacP2JacP is correct: %d\n", a&b&c)
}

func testEcdlpPointAddPoint() {
	var res = JacPoint{BN{1}, BN{1}, BN{0}}

	JacPAddJacP2JacP(&res, &Sm2JacG, &testJac2GMont)

	var a = BnEqual(&(testJac3GMont.x), &(res.x), Sm2CurveDigit)
	var b = BnEqual(&(testJac3GMont.y), &(res.y), Sm2CurveDigit)
	var c = BnEqual(&(testJac3GMont.z), &(res.z), Sm2CurveDigit)

	fmt.Printf("Test JacPAddJacP2JacP is correct: %d\n", a&b&c)
}

func testEcdlpNumMulPoint() {
	var res = JacPoint{BN{1}, BN{1}, BN{0}}
	var three = BN{3}
	NumMulJacP2JacP(&res, &three, &Sm2JacG)
	var a = BnEqual(&(testJac3GMont.x), &(res.x), Sm2CurveDigit)
	var b = BnEqual(&(testJac3GMont.y), &(res.y), Sm2CurveDigit)
	var c = BnEqual(&(testJac3GMont.z), &(res.z), Sm2CurveDigit)
	fmt.Printf("Test NumMulJacP2JacP is correct: %d\n", a&b&c)
}

func testEcdlpJacToAff() {
	var res = JacPoint{BN{1}, BN{1}, BN{0}}
	var priKey BN
	var pubKeyX, pubKeyY BN
	var pubKeyXStd, pubKeyYStd BN

	BytesToBn(&priKey, Sm2CurveDigit, testPriKeyBytesStd, Sm2CurveSize)
	BytesToBn(&pubKeyXStd, Sm2CurveDigit, testPubKeyBytesStd[:Sm2CurveSize], Sm2CurveSize)
	BytesToBn(&pubKeyYStd, Sm2CurveDigit, testPubKeyBytesStd[Sm2CurveSize:], Sm2CurveSize)

	NumMulJacP2JacP(&res, &priKey, &Sm2JacG)
	JacP2AffC(&pubKeyX, &pubKeyY, &res)

	var a = BytesEqual(BnToBytes(&pubKeyX, Sm2CurveDigit), testPubKeyBytesStd[:Sm2CurveSize], Sm2CurveSize)
	var b = BytesEqual(BnToBytes(&pubKeyY, Sm2CurveDigit), testPubKeyBytesStd[Sm2CurveSize:], Sm2CurveSize)

	fmt.Printf("Test JacP2AffC is correct: %d\n", a&b)
}

func testSm2Sign() {
	z, pubKeyBytes := Sm2KeyGen(testPriKeyBytesStd)
	a := BytesEqual(pubKeyBytes, testPubKeyBytesStd, Sm2CurveSize*2)
	//fmt.Printf("Test Sm2KeyGen is correct: %v\n", z == 0 && a == 1)

	b, sig := Sm2SignHash(testPriKeyBytesStd, testRnd, testHashStd)
	c := BytesEqual(sig, testSignStd, Sm2CurveSize*2)
	//fmt.Printf("Test Sm2SignHash is correct: %v\n", b == 0 && c == 1)

	d := Sm2VrfHashSign(testHashStd, pubKeyBytes, sig)
	//fmt.Printf("Test Sm2VrfHashSign is correct: %v\n", d == 0)

	fmt.Printf("SM2 digital signature is correct tested by standard data: ")
	fmt.Println(z == 0 && a == 1 && b == 0 && c == 1 && d == 0)
}

func testSm2SignRandom() {
	times := 1000

	for i := 0; i < times; i++ {
		rnd := GetRandomBytes(Sm2CurveSize)
		hash := GetRandomBytes(Sm2CurveSize)

		b, sig := Sm2SignHash(testPriKeyBytesStd, rnd, hash)

		for b != 0 {
			b, sig = Sm2SignHash(testPriKeyBytesStd, rnd, hash)
		}

		c := Sm2VrfHashSign(hash, testPubKeyBytesStd, sig)

		if c != 0 {
			fmt.Println("SM2 digital signature is correct tested by random data: Fail!!!!!!")
			return
		}
	}

	fmt.Println("SM2 digital signature is correct tested by random data: Success")
}

func TestSm2() {
	fmt.Println("+------------------------------------------------------------------------------+")
	fmt.Println("+---    Test module  --->  sm2")
	testEcdlpDoublePoint()
	testEcdlpPointAddPoint()
	testEcdlpNumMulPoint()
	testEcdlpJacToAff()
	testSm2Sign()
	testSm2SignRandom()
	fmt.Println("+------------------------------------------------------------------------------+")
}

func benchEcdlpNumMulPoint() {
	var res = JacPoint{BN{1}, BN{1}, BN{0}}
	var rnd BN
	var cost int64 = 0

	var i, j int
	var start time.Time
	for i = 0; i < 100; i++ {
		BytesToBn(&rnd, Sm2CurveDigit, GetRandomBytes(Sm2CurveSize), Sm2CurveSize)
		start = time.Now()
		for j = 0; j < 10; j++ {
			NumMulJacP2JacP(&res, &rnd, &Sm2JacG)
		}
		// 单位：微秒
		cost += time.Since(start).Microseconds()
	}
	fmt.Printf("bench NumMulJacP2JacP: %.2f us\n", float64(cost)/1000.0)
}

func benchSm2SignKeyGen() {
	var cost int64 = 0

	var i, j int
	var start time.Time
	for i = 0; i < 100; i++ {
		rnd := GetRandomBytes(Sm2CurveSize)
		start = time.Now()
		for j = 0; j < 10; j++ {
			Sm2KeyGen(rnd)
		}
		// 单位：微秒
		cost += time.Since(start).Microseconds()
	}
	fmt.Printf("bench Sm2SignKeyGen: %.2f us\n", float64(cost)/1000.0)
}

func benchSm2SignSigHash() {
	var cost int64 = 0

	var i, j int
	var start time.Time
	for i = 0; i < 100; i++ {
		priKeyBytes := GetRandomBytes(Sm2CurveSize)
		rnd := GetRandomBytes(Sm2CurveSize)
		hash := GetRandomBytes(Sm2CurveSize)

		start = time.Now()
		for j = 0; j < 10; j++ {
			Sm2SignHash(priKeyBytes, rnd, hash)
		}
		// 单位：微秒
		cost += time.Since(start).Microseconds()
	}
	fmt.Printf("bench Sm2SignSigHash: %.2f us\n", float64(cost)/1000.0)
}

func benchSm2SignVrfHash() {
	var cost int64 = 0

	var i, j int
	var start time.Time
	for i = 0; i < 100; i++ {
		start = time.Now()
		for j = 0; j < 10; j++ {
			Sm2VrfHashSign(testHashStd, testPubKeyBytesStd, testSignStd)
		}
		// 单位：微秒
		cost += time.Since(start).Microseconds()
	}
	fmt.Printf("bench Sm2SignVrfHash: %.2f us\n", float64(cost)/1000.0)
}

func BenchSm2() {
	fmt.Println("+------------------------------------------------------------------------------+")
	fmt.Println("+---    Bench module  --->  sm2")
	benchEcdlpNumMulPoint()
	benchSm2SignKeyGen()
	benchSm2SignSigHash()
	benchSm2SignVrfHash()
	fmt.Println("+------------------------------------------------------------------------------+")
}
